namespace SimpleWebServices.Web.UI.Html
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Xml;
    using System.Xml.Schema;
    using System.Xml.Serialization;
    using SimpleWebServices;
    using SimpleWebServices.Web.UI.Html.Elements;

    [XmlRoot("metadata")]
    public sealed class MetaCollection : IEnumerable<Meta>, IXmlSerializable
    {
        public MetaCollection()
        {
            this.Data = new Collection<Meta>();
        }

        public int Count
        {
            get
            {
                return this.Data.Count;
            }
        }

        private Collection<Meta> Data
        {
            get;
            set;
        }

        public Meta this[string name]
        {
            get
            {
                Meta value = this.Data
                    .Where(x => name.Equals(x.Name) || name.Equals(x.HttpEquivalent))
                    .FirstOrDefault();

                if (null == value)
                {
                    throw new KeyNotFoundException(name);
                }

                return value;
            }
        }

        public static bool operator ==(MetaCollection obj, MetaCollection comparand)
        {
            bool result = false;

            if (object.ReferenceEquals(null, obj))
            {
                result = object.ReferenceEquals(null, comparand);
            }
            else
            {
                result = obj.Equals(comparand);
            }

            return result;
        }

        public static bool operator !=(MetaCollection obj, MetaCollection comparand)
        {
            bool result = false;

            if (object.ReferenceEquals(null, obj))
            {
                result = !object.ReferenceEquals(null, comparand);
            }
            else
            {
                result = !obj.Equals(comparand);
            }

            return result;
        }

        public void Add(MetaCollection data)
        {
            if (null == data)
            {
                throw new ArgumentNullException("data");
            }

            foreach (Meta meta in data)
            {
                this.Data.Add(meta);
            }
        }

        public void Add(Meta meta)
        {
            if (null == meta)
            {
                throw new ArgumentNullException("meta");
            }

            this.Data.Add(meta);
        }

        public bool Contains(string name)
        {
            if (null == name)
            {
                throw new ArgumentNullException("name");
            }
            else if (0 == name.Length)
            {
                throw new ArgumentOutOfRangeException("name");
            }

            return 0 != this.Data
                .Where(x => name.Equals(x.Name) || name.Equals(x.HttpEquivalent))
                .Count();
        }

        public bool Contains(Meta meta)
        {
            bool result = false;

            if (null != meta)
            {
                foreach (var item in this.Data)
                {
                    if ((!string.IsNullOrEmpty(item.Name) && item.Name == meta.Name)
                        || (!string.IsNullOrEmpty(item.HttpEquivalent) && item.HttpEquivalent == meta.HttpEquivalent))
                    {
                        result = true;
                        break;
                    }
                }
            }

            return result;
        }

        public override bool Equals(object obj)
        {
            bool result = false;

            if (!object.ReferenceEquals(null, obj))
            {
                if (object.ReferenceEquals(this, obj))
                {
                    result = true;
                }
                else
                {
                    MetaCollection cast = obj as MetaCollection;

                    if (!object.ReferenceEquals(null, cast) && this.Count == cast.Count)
                    {
                        result = true;
                        foreach (Meta meta in this)
                        {
                            if (!cast.Data.Contains(meta))
                            {
                                result = false;
                                break;
                            }
                        }
                    }
                }
            }

            return result;
        }

        public override int GetHashCode()
        {
            return this.ToString().GetHashCode();
        }

        public override string ToString()
        {
            return this.Serialize().CreateNavigator().OuterXml;
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return (this.Data as IEnumerable).GetEnumerator();
        }

        IEnumerator<Meta> IEnumerable<Meta>.GetEnumerator()
        {
            return (this.Data as IEnumerable<Meta>).GetEnumerator();
        }

        XmlSchema IXmlSerializable.GetSchema()
        {
            throw new NotSupportedException();
        }

        void IXmlSerializable.ReadXml(XmlReader reader)
        {
            if (null == reader)
            {
                throw new ArgumentNullException("reader");
            }

            if (reader.IsEmptyElement)
            {
                reader.Read();
            }
            else
            {
                string name = reader.Name;
                reader.Read();
                while (true)
                {
                    if (XmlNodeType.None == reader.NodeType)
                    {
                        break;
                    }
                    else if (XmlNodeType.EndElement == reader.NodeType && reader.Name == name)
                    {
                        reader.Read();
                        break;
                    }
                    else if ("meta".Equals(reader.Name, StringComparison.OrdinalIgnoreCase))
                    {
                        this.Add(reader.ReadOuterXml().Deserialize<Meta>());
                    }
                    else
                    {
                        reader.Read();
                    }
                }
            }
        }

        void IXmlSerializable.WriteXml(XmlWriter writer)
        {
            if (null == writer)
            {
                throw new ArgumentNullException("writer");
            }

            foreach (Meta meta in this.Data)
            {
                writer.WriteRaw(meta.Serialize().CreateNavigator().OuterXml);
            }
        }
    }
}